﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;

namespace DBA.Common
{
    public class DynamicBuilder<T>
    {
        public static object LockObj;
        public static Dictionary<int, DynamicBuilder<T>> DynBuilderDict;
		private delegate T Load(IDataRecord dataRecord);
		private static readonly MethodInfo getValueMethod = typeof(IDataRecord).GetMethod("get_Item", new Type[]
		{
			typeof(int)
		});
		private static readonly MethodInfo isDBNullMethod = typeof(IDataRecord).GetMethod("IsDBNull", new Type[]
		{
			typeof(int)
		});
		private DynamicBuilder<T>.Load handler;
		private DynamicBuilder()
		{
		}
        /// <summary>
        /// 执行CreateBuilder里创建的DynamicCreate动态方法的委托
        /// </summary>
        /// <param name="dataRecord"></param>
        /// <returns></returns>
		public T Build(IDataRecord dataRecord)
		{
			return this.handler(dataRecord);
		}
		public static DynamicBuilder<T> CreateBuilder(IDataRecord dataRecord)
		{
			DynamicBuilder<T> dynamicBuilder = new DynamicBuilder<T>();
			DynamicMethod dynamicMethod = new DynamicMethod("DynamicCreate", typeof(T), new Type[]
			{
				typeof(IDataRecord)
			}, typeof(T), true);
			ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
			LocalBuilder local = iLGenerator.DeclareLocal(typeof(T));
			iLGenerator.Emit(OpCodes.Newobj, typeof(T).GetConstructor(Type.EmptyTypes));
			iLGenerator.Emit(OpCodes.Stloc, local);
			checked
			{
				for (int i = 0; i < dataRecord.FieldCount; i++)
				{
                    PropertyInfo property = typeof(T).GetProperty(StringHelper.StringFirstCharToUpper(dataRecord.GetName(i)));
					Label label = iLGenerator.DefineLabel();
					if (property != null && property.GetSetMethod() != null)
					{
						iLGenerator.Emit(OpCodes.Ldarg_0);
						iLGenerator.Emit(OpCodes.Ldc_I4, i);
						iLGenerator.Emit(OpCodes.Callvirt, DynamicBuilder<T>.isDBNullMethod);
						iLGenerator.Emit(OpCodes.Brtrue, label);
						iLGenerator.Emit(OpCodes.Ldloc, local);
						iLGenerator.Emit(OpCodes.Ldarg_0);
						iLGenerator.Emit(OpCodes.Ldc_I4, i);
						iLGenerator.Emit(OpCodes.Callvirt, DynamicBuilder<T>.getValueMethod);
						iLGenerator.Emit(OpCodes.Unbox_Any, dataRecord.GetFieldType(i));
						iLGenerator.Emit(OpCodes.Callvirt, property.GetSetMethod());
						iLGenerator.MarkLabel(label);
					}
				}
				iLGenerator.Emit(OpCodes.Ldloc, local);
				iLGenerator.Emit(OpCodes.Ret);
				dynamicBuilder.handler = (DynamicBuilder<T>.Load)dynamicMethod.CreateDelegate(typeof(DynamicBuilder<T>.Load));
				return dynamicBuilder;
			}
		}

        /// <summary>
        /// 得到一个创建者
        /// </summary>
        /// <param name="dataRecord"></param>
        /// <returns></returns>
        public static DynamicBuilder<T> CreateBuilderAndSave(IDataRecord dataRecord)
        {
            Type returnType = typeof(T);
            int hashCode = returnType.GetHashCode();
            lock (LockObj)
            {
                if (DynBuilderDict.ContainsKey(hashCode))
                {
                    return DynBuilderDict[hashCode];
                }
            }
            DynamicBuilder<T> DynamicBuilder = new DynamicBuilder<T>();
            //定义一个名为'DynamicCreateAndSave'的动态方法，返回值typof(T)，参数typeof(IDataRecord),与typeof(IDataRecord)逻辑关联{动态方法可以访问类型的所有成员。 },
            DynamicMethod method = new DynamicMethod("DynamicCreateAndSave", returnType, new Type[] { typeof(IDataRecord) }, returnType, true);
            //创建一个MSIL生成器，为动态方法生成代码
            ILGenerator iLGenerator = method.GetILGenerator();
            //为动态方法声明指定类型T的局部变量 T result;
            LocalBuilder local = iLGenerator.DeclareLocal(returnType);
            //可以t=new T();这么理解
            iLGenerator.Emit(OpCodes.Newobj, returnType.GetConstructor(Type.EmptyTypes));
            iLGenerator.Emit(OpCodes.Stloc, local);
            for (int i = 0; i < dataRecord.FieldCount; i++) //遍历字段的数据集合
            {
                //根据列名取属性  原则上属性和列是一一对应的关系
                PropertyInfo property = returnType.GetProperty(dataRecord.GetName(i));
                Label label = iLGenerator.DefineLabel();
                //实体存在该属性 且该属性有SetMethod方法
                if (property != null && property.GetSetMethod() != null)
                {
                    iLGenerator.Emit(OpCodes.Ldarg_0);
                    iLGenerator.Emit(OpCodes.Ldc_I4, i);
                    //就知道这里要调用IsDBNull方法 如果IsDBNull==true contine
                    iLGenerator.Emit(OpCodes.Callvirt, isDBNullMethod);
                    iLGenerator.Emit(OpCodes.Brtrue, label);
                    iLGenerator.Emit(OpCodes.Ldloc, local);
                    iLGenerator.Emit(OpCodes.Ldarg_0);
                    iLGenerator.Emit(OpCodes.Ldc_I4, i);
                    //调用get_Item方法
                    iLGenerator.Emit(OpCodes.Callvirt,getValueMethod);
                    iLGenerator.Emit(OpCodes.Unbox_Any, dataRecord.GetFieldType(i));
                    //给该属性设置对应值
                    iLGenerator.Emit(OpCodes.Callvirt, property.GetSetMethod());
                    iLGenerator.MarkLabel(label);
                }
            }
            iLGenerator.Emit(OpCodes.Ldloc, local);
            //方法结束，返回
            iLGenerator.Emit(OpCodes.Ret);
            DynamicBuilder.handler = (DynamicBuilder<T>.Load)method.CreateDelegate(typeof(DynamicBuilder<T>.Load));
            lock (LockObj)
            {
                if (DynBuilderDict.ContainsKey(hashCode))
                {
                    DynBuilderDict.Add(hashCode, DynamicBuilder);
                }
            }
            return DynamicBuilder;
        }
    }
}
